package com.type.TypingDetector;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class TrainingScreen extends Activity {
	private DataArrObj[] dao;
	private SensorManager sm;
    private List<Sensor> sensors;
    private Sensor accel;
	private boolean accelerometerAvailable=false;
	private boolean currentlyWatching=false;
	private String classTitle="";
	
	private final int DATA_MEMORY=2000;
	private final int START_OFFSET=5;
	private final int WAIT_OFFSET=20;
    private int counter;
    private final int SMOOTH_ITERATIONS=1;
    private int watchCounter=-1;
    private int startCounter=-1;
    
	private Button topButton, leftButton, rightButton, bottomButton;
	private TextView disp;
	
	//TCP Communication Stuff
    private boolean mSocketAvl=false;
    private Socket socket; //Socket used to communicate with server
    private String serverPacket;
    private final String mIPAddr="128.2.142.39";
    private final int portNo=6002;
    private BufferedReader in;
    private PrintWriter out;
    private final String TRANSMIT_OVER="Done";
    private final String DATABASE_DATA_FN="DBDat";
    private final String CLASSES="Right, Left, Top, Bottom";
    private boolean exporting=false;
    
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.four_button);
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
		
		dao=new DataArrObj[DATA_MEMORY];
		sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        sensors = sm.getSensorList(Sensor.TYPE_ACCELEROMETER);
        
        topButton=(Button) findViewById(R.id.top_button);
        rightButton=(Button) findViewById(R.id.right_button);
        leftButton=(Button) findViewById(R.id.left_button);
        bottomButton=(Button) findViewById(R.id.bottom_button);
        disp=(TextView) findViewById(R.id.dispBut);
        disp.setText("Hi");
        
        topButton.setOnClickListener(btListener);
        rightButton.setOnClickListener(btListener);
        leftButton.setOnClickListener(btListener);
        bottomButton.setOnClickListener(btListener);
        
        if (sensors.size() > 0) {
        	accel = sensors.get(0);
        	accelerometerAvailable=true;
        	sm.registerListener(mySEL, accel, SensorManager.SENSOR_DELAY_FASTEST);
        }
        counter=0;
	}
	
	private OnClickListener btListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			if (!currentlyWatching) {
				int diff=counter-START_OFFSET;
				if (diff<0) {startCounter=diff+DATA_MEMORY;}
				else {startCounter=diff;}
				watchCounter=(startCounter+WAIT_OFFSET)%dao.length;
				classTitle=""+((Button)v).getText();
				currentlyWatching=true;
			}
		}

		
		
	};
	
	private SensorEventListener mySEL = new SensorEventListener() {

		@Override
		public void onSensorChanged(SensorEvent event) {
			// TODO Auto-generated method stub
			double[] vals = {(double)event.values[0], (double)event.values[1], (double)event.values[2]};
			dao[counter] = new DataArrObj(vals);
			
			
			if (currentlyWatching) {
				if (counter==watchCounter) {
					DataArrObj[] temp;
					int diff = watchCounter-startCounter;
					if (diff>0) {temp=new DataArrObj[diff];} //if normal
					else {temp=new DataArrObj[diff+DATA_MEMORY];} //wrap around
					
					for (int i=0; i<temp.length; i++) {
						temp[i]=dao[(startCounter+i)%dao.length];
					}
					
					InfoDatObject ido=parseData(temp);
					if (mSocketAvl)
						out.println(writeServerPacket(ido));
					disp.setText(classTitle);
					currentlyWatching=false;
					classTitle="";
				}
			}
			
			counter++;
			counter%=dao.length;
		}
		
		@Override
		public void onAccuracyChanged(Sensor sensor, int accuracy) {
			// TODO Auto-generated method stub
			
		}
	};
	
	
	/** This method parses files to initialize the staticVals array with the data accrued from LearningMode.
	 * 
	 * @param strm The Input Stream of the files to be read
	 * @param index The PREDICTION_TYPE index indicating the type of data the current Stream is referring too
	 * @return A comparison object with the average and fundamental frequency
	 * @throws FileNotFoundException
	 */
	private InfoDatObject parseData(DataArrObj[] vals) {
		/*Scanner scan=new Scanner(strm);
		ArrayList<Double> data=new ArrayList<Double>(); //don't know size, so ArrayList
		scan.useDelimiter(",");
		scan.nextLine(); //throw out first line
		while (scan.hasNextLine()) { //must be formatted as xval, yval, zval
			String temp=scan.nextLine();
			if (temp!="") {
				double storVal = Double.valueOf(temp);
				data.add(storVal);
			}
		}*/
		
		double[] datX=new double[vals.length];
		double[] datY=new double[vals.length];
		double[] datZ=new double[vals.length];
		for (int j=0; j<vals.length; j++) {
			datX[j]=vals[j].vals[0];
			datY[j]=vals[j].vals[1];
			datZ[j]=vals[j].vals[2];
		}
		
		for (int j=0; j<SMOOTH_ITERATIONS; j++) {
			datX=AcceleromterUpdated.smoothValues(datX);
			datY=AcceleromterUpdated.smoothValues(datY);
			datZ=AcceleromterUpdated.smoothValues(datZ);
		}
		
		double[] datRX = new double[FourierTransform.nextPowerOfTwo(vals.length)];
		double[] datIX = new double[datRX.length];
		double[] datRY = new double[FourierTransform.nextPowerOfTwo(vals.length)];
		double[] datIY = new double[datRY.length];
		double[] datRZ = new double[FourierTransform.nextPowerOfTwo(vals.length)];
		double[] datIZ = new double[datRZ.length];
		
		double datMaxX=0,datMaxY=0,datMaxZ=0;
		double datMinX=Double.MAX_VALUE, datMinY=Double.MAX_VALUE, datMinZ=Double.MAX_VALUE;
		double cTotX=0.0, cTotY=0.0, cTotZ=0.0;
		
		for (int j=0; j<datRX.length; j++) {
		 	if (j<datX.length) {
		 		if (datX[j]>datMaxX) {
		 			datMaxX=datX[j];
		 		}
		 		if (datX[j]<datMinX) {
		 			datMinX=datX[j];
		 		}
		 		if (datY[j]>datMaxY) {
		 			datMaxY=datY[j];
		 		}
		 		if (datY[j]<datMinY) {
		 			datMinY=datY[j];
		 		}
		 		if (datZ[j]>datMaxZ) {
		 			datMaxZ=datZ[j];
		 		}
		 		if (datZ[j]<datMinZ) {
		 			datMinZ=datZ[j];
		 		}
		 		datRX[j] = datX[j]; datRY[j] = datY[j]; datRZ[j] = datZ[j];
				cTotX+=datRX[j]; cTotY+=datRY[j]; cTotZ+=datRZ[j];
			}
			else { //pad with 0s
				datRX[j]=0.0; datRY[j]=0.0; datRZ[j]=0.0;
			}
			datIX[j]=0.0; datIY[j]=0.0; datIZ[j]=0.0;
		}
		
		double avgX = cTotX/(vals.length), avgY=cTotY/(vals.length), avgZ=cTotZ/(vals.length);
		FastFourierTransform.fastFT(datRX, datIX, true);
		FastFourierTransform.fastFT(datRY, datIY, true);
		FastFourierTransform.fastFT(datRZ, datIZ, true);
		double fFreqX = findAvgFundamentalFreq(datRX, datIX);
		double fFreqY = findAvgFundamentalFreq(datRY, datIY);
		double fFreqZ = findAvgFundamentalFreq(datRZ, datIZ);
		//scan.close();
		
		return new InfoDatObject(new double[] {fFreqX, fFreqY, fFreqZ}, new double[] {avgX, avgY, avgZ}, new double[] {datMaxX, datMaxY, datMaxZ}, new double[] {datMinX, datMinY, datMinZ});
	}
	
	public double findAvgFundamentalFreq(double[] real, double[] imag) {
		double retVal=-1;
		double[] maxes=new double[3];
		double minMax=0;
		int maxCounter=0;
		
		for (int i=0; i <real.length; i++) {
			double mag = Math.sqrt(Math.pow(real[i],2)+Math.pow(imag[i],2));
			if (mag > minMax) {
				if (maxCounter<2) {
					maxes[maxCounter] = mag;
					maxCounter++;
				}
				else {
					//find lowest of the current maxes
					double min=Double.MAX_VALUE;
					int indexOfLowest=0;
					for (int j=0; j<maxes.length;j++) {
						if (maxes[j]<min) {min=maxes[j];}
						indexOfLowest=j;
					}
					maxes[indexOfLowest]=mag;
					minMax=mag;
				}
			}		
		}
		return (double)((maxes[0]+maxes[1]+maxes[2])/3);
	}
	
	private String writeServerPacket(InfoDatObject ido) {
		String retVal="";
		String dlm=",";
		String titleReplacement=" ";
		double[] fFreqs=ido.getfFreqs();
		double[] avs=ido.getAvgs();
		double[] mxs=ido.getMaxes();
		double[] mns=ido.getMins();
		
		retVal+=fFreqs[0] + dlm + fFreqs[1] + dlm + fFreqs[2] +
			dlm + avs[0] + dlm + avs[1] + dlm + avs[2] + dlm + mxs[0] + dlm +
			mxs[1] + dlm + mxs[2] + dlm + mns[0] + dlm + mns[1] + dlm + mns[2] + dlm + classTitle;
		
		return retVal;
	}
	
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
	    MenuInflater inflater = getMenuInflater();
	    inflater.inflate(R.layout.menu_export, menu);
	    return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
	    // Handle item selection
	    switch (item.getItemId()) {
	    case R.id.export:
	    	exporting=!exporting;
	        if (exporting) initializeServerComm();
	        else shutDownComm();
	        return true;
	    default:
	        return super.onOptionsItemSelected(item);
	    }
	}
	
	private void initializeServerComm() {
		disp.setText("Export");
		try {
			if (socket==null) {
				socket=new Socket(mIPAddr, portNo);
				in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
				out=new PrintWriter(socket.getOutputStream(), true);
				disp.setText("Connected");
				mSocketAvl=true;
			}
    	 }
    	 catch (IOException e) {
    	 	mSocketAvl=false;
    	 	disp.setText("Failure");
    	 }
    	 
    	 if (mSocketAvl) {
    		 out.println(DATABASE_DATA_FN);
    		 out.println(CLASSES);
    		 out.println("L");
    	 }
	}
	
	private void shutDownComm() {
		disp.setText("End Export");
		if (mSocketAvl) {
			out.println(TRANSMIT_OVER);
			try {
				socket.close();
				in.close();
				socket=null;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			out.close();
			mSocketAvl=false;
		}
	}
	
	@Override
	protected void onDestroy() {
		if (mSocketAvl) {
			shutDownComm();
		}
		super.onDestroy();
	}
	
	private class DataArrObj {
		private double[] vals;
		private long time;
		
		public DataArrObj(double[] v) {
			this.setVals(v);
			this.time=SystemClock.uptimeMillis();
		}

		public void setVals(double[] val) {
			this.vals = val;
		}

		public double[] getVals() {
			return vals;
		}

		public void setTime(long time) {
			this.time = time;
		}

		public long getTime() {
			return time;
		}
	}
	
	private class InfoDatObject {
		private String name;
		private double[] fFreqs;
		private double[] avgs;
		private double[] maxes;
		private double[] mins;
		
		
		public InfoDatObject(double[] freqs, double[] avs, double[] mxs, double[] mns) {
			setfFreqs(freqs);
			setAvgs(avs);
			setMaxes(mxs);
			setMins(mns);
		}
		
		public InfoDatObject(String nam, double[] freqs, double[] avs, double[] mxs, double[] mns) {
			setName(nam);
			setfFreqs(freqs);
			setAvgs(avs);
			setMaxes(mxs);
			setMins(mns);
		}
		
		public void setfFreqs(double[] fFreqs) {
			this.fFreqs = fFreqs;
		}
		public double[] getfFreqs() {
			return fFreqs;
		}
		public void setAvgs(double[] avgs) {
			this.avgs = avgs;
		}
		public double[] getAvgs() {
			return avgs;
		}
		public void setMaxes(double[] maxes) {
			this.maxes = maxes;
		}
		public double[] getMaxes() {
			return maxes;
		}
		public void setMins(double[] mins) {
			this.mins = mins;
		}
		public double[] getMins() {
			return mins;
		}
		public void setName(String nam) {
			this.name = nam;
		}
		public String getName() {
			return name;
		}
	}
}
